#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <fcntl.h>

#include "../includes/clientInfo.h"

//#define		HOST	"192.168.50.136"			//服务器的P地址
//#define		PORT	6666									 //进程端口号，端口号是数字类型，ip地址是字符串类型
#define		MAX_SIZE		 10*1024					 //缓存容量
#define		MAX_CLIENT	  5									 //最大可接受客户端数量
int running = 1;														  //全局变量控制while()是否结束
int read_runing = 1;												 //控制read线程中的循环是否结束
int rece_runing = 1;												 //控制rece线程中的循环是否结束

clientList list;															  //全局变量：客户端链表，将所有客户端信息串联起来,需要进行临界保护
pthread_mutex_t mutex;										//互斥锁，保护全局变量list

//字符串处理函数，将格式"fd=**,……"的字符串改成格式"……"
void remove_first_n_chars(char *str, int n) {  
    if (n <= 0) return; // 无需处理  
    int len = strlen(str);  
    if (n >= len) {  
        str[0] = '\0'; // 清空字符串  
        return;  
    }  
    // 移动字符  
    for (int i = 0; i < len - n; i++) {  
        str[i] = str[i + n];  
    }  
    str[len - n] = '\0'; // 添加终止符  
}  

//要执行的读线程函数，每一个客户端都需要创建一个读线程监听
void	*Server_Read(void	*arg){
	int res;																											   //上锁结果
	char pthread_buffer[MAX_SIZE];															//接收缓存
	for(;read_runing != 0;)
	{
		memset(pthread_buffer,0,sizeof(pthread_buffer));				//清空接收缓存
		res = pthread_mutex_lock(&mutex);											   //上锁，获得当前所有客户端信息
		clientList rece_list = list;
 		pthread_mutex_unlock(&mutex);													 //解锁		
		int input_connfd = (unsigned long long)arg;								  //创建读线程时，已经将已连接标识符作为传递参数，用于查找链表中对应的客户端
		newClient* foundClient = findClient(&rece_list, input_connfd);
		if(foundClient){																						//找到对应客户端，则以阻塞read的方式等待读取信息
			if(read(foundClient->connfd,pthread_buffer,sizeof(pthread_buffer))	<=	0){
					printf("connfd = %d对应的客户端关闭……\n",input_connfd);
					close(foundClient->connfd);
					res = pthread_mutex_lock(&mutex);									 //上锁
					removeClient(&list,foundClient->connfd);					     //客户端已关闭，则从客户端链表中删除
					pthread_mutex_unlock(&mutex);										  //解锁
					break;
				}
				else{
					printf("来自客户端（%s：%d）：%s\n",inet_ntoa((foundClient->clientData).sin_addr),ntohs((foundClient->clientData).sin_port),pthread_buffer);
				}
		}
	}
	///退出线程
	pthread_exit(NULL);
}

//要执行的写线程函数
void	*Server_Write(void	*arg)
{
	struct	sockaddr_in	server;
	int len_sockaddr = sizeof(struct sockaddr);
	for (; ; )
	{
		char pthread_buffer[MAX_SIZE];
		fgets(pthread_buffer,MAX_SIZE,stdin);
		//输入了exit,循环结束
		if(strncmp(pthread_buffer,"exit",4)	==	0)
		{
			printf("本地服务端关闭……\n");
			break;
		}
		int sendfd = (pthread_buffer[3] - '0')*10 + (pthread_buffer[4] - '0');												//发送数据有格式要求：fd=**，……
		getsockname(sendfd, (struct sockaddr *)&server, &len_sockaddr);												//获取服务器的本地地址
		remove_first_n_chars(pthread_buffer,6);																									//截取掉前面6个字符，fd=**,
		write(sendfd,pthread_buffer,sizeof(pthread_buffer));																		//服务器写
		printf("服务器端（%s：%d）发送数据：%s\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port),pthread_buffer);
		//printf("服务器端发送数据：%s\n",pthread_buffer);
		memset(pthread_buffer,0x00,sizeof(pthread_buffer));																		 //清空发送缓存
	}
	//退出线程
	read_runing = 0;																																						//所有读线程终止
	rece_runing = 0;
	running = 0;
	pthread_exit(NULL);
}

//要执行的监听新客户端接入服务器的线程函数
void	*Server_ReceNewClient(void	*arg)
{
	int res;
	clientList rece_list = (clientList)(*((clientList*)arg));						//传入的参数，客户端链表，如果传入参数就用这种方式，这里获得初始的链表，初始的链表在线程创建前写入了监听fd信息
	int input_sockfd = rece_list.listenfd;												      //得到监听的fd号
	struct	sockaddr_in	client;												  							//存放连接时得到的客户端信息
	unsigned int len_sockaddr = sizeof(struct sockaddr);			  	  //sockaddr_in结构体大小

	pthread_t	thread_ServerRead[MAX_CLIENT],thread_ServerWrite;	//读线程标识符数组，写线程标识符	
	int thread_read,thread_write;																 //线程创建结果
	static int n = 0;																								//用于计数读创建了几个线程

	//创建服务器的写线程,一个服务器只要一个写线程就够了
	thread_write = pthread_create(&thread_ServerWrite,NULL,Server_Write,NULL);
	if(thread_write){
		printf("服务器的写线程创建失败\n");
	}
	else{
		printf("服务器的写线程创建成功，线程开始运行\n");	
	}

	//进入循环
	for(;rece_runing != 0;)
	{
		bzero(&client,sizeof(struct sockaddr_in));										//清空
		//处理客户端连接请求，线程卡在这儿阻塞，用于等待新客户端申请连接
		int connfd = accept(input_sockfd,(struct	sockaddr*)&client,&len_sockaddr);
		if(connfd	<	0){
			printf("本次服务器处理客户端连接请求处理失败……\n");
		}
		else{																																												 
			printf("新连接的客户端地址：%s：%d，它的connfd(通信编号) = %d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),connfd);

			//新接入的客户端按connfd大小顺序插入全局变量：客户端链表中
			res = pthread_mutex_lock(&mutex);												//上锁
			insertInOrder(&list,connfd,client); 												  //新客户端传入客户端链表
			//printList(&list);																						 //遍历
 			pthread_mutex_unlock(&mutex);													//解锁

			//创建服务器的读线程，并将已连接套接字标识符作为参数输入，通过connfd便可找到链表中对应的客户端
			thread_read = pthread_create(&thread_ServerRead[n],NULL,Server_Read,(void*)(unsigned long long)(connfd));
			if(thread_read){
				printf("connfd = %d，对应客户端的读线程创建失败\n",connfd);
			}
			else{
				printf("connfd = %d，对应客户端的读线程创建成功\n",connfd);	
			}	
			//printf("n = %d\n",n);
			n++;	//为下次新客户端的连接准备					
			//检查服务器连接的客户端数量是否达到上限
			if(n >= MAX_CLIENT)
			{
				printf("\n	！！！注意连接的客户端数量达到上限！！！\n");			
			}	
		}
	}
	//退出线程
	pthread_exit(NULL);
} 

int	main(int argc,char* argv[])
{

 	int	server_port	=	0;
	int	sockfd;													 												  					//监听套接字和已连接套接字，Server监听套接字只需要一个，但已连接套接字每个客户端需要一个
	struct	sockaddr_in	server;				  													    			  //主要存放服务器、客户端的协议族、ip地址和端口号
	struct sockaddr_in listenAddr;		     							  									 //监听的地址、连接的本地地址、连接的对方的地址
	unsigned int len_sockaddr;																				   //sockaddr_in结构体大小
	pthread_t	thread_ReceNewClient;			   													   //线程变量，服务器接收新客户端的线程，作用：创建一个服务器写线程，每一个客户端进入创建一个对应的读线程
	int	thread_newclient;	 					   			    													  //线程有关的结果
	len_sockaddr = sizeof(struct sockaddr);															//获取结构体的大小

	if (argc > 1)
	{
		server_port	=	atoi(argv[1]);
		printf("本地服务器IP可以通过ifconfig查询\n");
		printf("设置的server_port = %d\n",server_port);
	}
	else
	{
		printf("缺少参数，请在./main后输入参数‘服务器端口号’\n");
		exit(1);
	}

	//创建套接字描述符
	if((sockfd	=	socket(AF_INET,SOCK_STREAM,0))	==	-1)
	{
		printf("创建一个用于监听的套接字失败 !\n");
		exit(1);
	}
	printf("创建一个用于监听的套接字成功\n");

	bzero(&server,sizeof(server));
	server.sin_family	=	AF_INET;									//设置服务器端参数
	server.sin_port	=	htons(server_port);
	server.sin_addr.s_addr = htonl(INADDR_ANY);		//允许服务器不必关心具体的 IP 地址，而是接受所有网络接口上的连接请求
	//绑定套接字,监听端口
	if(bind(sockfd,(struct	sockaddr *)&server,len_sockaddr)	!=	0)
	{
		printf("监听套接字绑定失败……\n");
		exit(1);
	}
	printf("监听套接字绑定成功……\n");

	//服务器开始监听
	if((listen(sockfd,MAX_CLIENT))	!=	0)
	{
		printf("监听失败……\n");
		exit(1);
	}
	getsockname(sockfd,(struct sockaddr*)&listenAddr,&len_sockaddr);
	printf("服务器开始监听……\n监听地址：%s：%d\n",inet_ntoa(listenAddr.sin_addr),ntohs(listenAddr.sin_port));		//打印出监听的地址
	printf("服务器可以接收来自不同客户端的数据；并且键入fd=**,……可以直接发送数据给对应的客户端：");

	//创建一个线程，用于接收新的客户端信息，当有新的客户端接入时
	initClientList(&list);					//初始化客户端链表
	list.listenfd = sockfd;
	thread_newclient = pthread_create(&thread_ReceNewClient,NULL,Server_ReceNewClient,&list);
	if(thread_newclient)
	{
		printf("服务器的接收客户端请求连接线程创建失败\n");	
	}
	else
	{
		printf("服务器的接收客户端请求连接线程创建成功\n");			
	}
	 while (running);
	close(sockfd);					
	exit(0); 
}